//==================================================================
//  Copyright (C) 2006-2007  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
/// @file
///
///
/// 
/// @author Davide Pasca
/// 
//==================================================================

#ifndef REND_SHADERS_H
#define REND_SHADERS_H

#include "pmatrix.h"
#if defined(TARGET_D3D)
	#include "rend_shaders_d3d.h"

#elif defined(TARGET_OPENGL)
	#include <Cg/Cg.h>
	#include <Cg/cgGL.h>

#endif
#include "texture_base.h"

//==================================================================
#define RENDSHADER_MAKECODE(_SHADER_SOURCE_)	#_SHADER_SOURCE_

//==================================================================
class RendDevice;
class RendShaderManager;

//==================================================================
///
//==================================================================
class RendShader
{
	RendShaderManager		*_managerp;

public:
#if defined(TARGET_D3D)
	//
#elif defined(TARGET_OPENGL)
	typedef CGprogram	Program;
	typedef CGparameter Param;

#endif

private:
	struct ParamInfo
	{
		ParamInfo(){}

		bool	_is_original;
		Param	_prog_param;
		char	_param_name[32];
	};

public:
	Program					_program;
private:
	std::vector<ParamInfo>	_params_list;

public:
	RendShader( RendShaderManager *managerp );
	~RendShader();

	void	CreatePixelProgram( const char *sourcep, const char *entrypointp=NULL );
	void	CreateVertexProgram( const char *sourcep, const char *entrypointp=NULL );
	Param	AddParam( const char *param_namep );
	Param	AddParamShared( const char *param_namep );
	Param	CloneParam( const char *param_namep, RendShader *from_shaderp );
	int		findParamIdxByName( const char *param_namep );
	Param	GetParamByName( const char *param_namep );
};

//==================================================================
///
//==================================================================
class RendShaderManager
{
	RendDevice				*_rdevicep;
	PMath::Matrix44			_cur_proj_mat;

	RendShader				*_cur_vtx_shaderp;
	RendShader				*_cur_pix_shaderp;

	//==================================================================
	///
	//==================================================================
public:
	class Std
	{
	public:
		RendShaderManager		*_managerp;

		float					_global_specular_coe;
		autodel<RendShader>	_std_vs_shaderp[2][2][2];
		autodel<RendShader>	_std_ps_shaderp[2][2];

		RendShader::Param		_VSP_U_proj_obj_m;
		RendShader::Param		_VSP_U_view_obj_m;
		RendShader::Param		_VSP_U_light_pos;
		RendShader::Param		_VSP_U_diffuse_col;
		RendShader::Param		_VSP_U_emissive_col;
		RendShader::Param		_VSP_U_r_light_obj_m;
		RendShader::Param		_VSP_U_g_light_obj_m;
		RendShader::Param		_VSP_U_b_light_obj_m;
		RendShader::Param		_VSP_U_PART_SIZE;	
		RendShader::Param		_VSP_U_fog_range;	

		RendShader::Param		_FSP_U_color_texmap[2];
		RendShader::Param		_cur_FSP_U_color_texmap;

		RendShader::Param		_FSP_U_fog_col;	
		RendShader::Param		_FSP_U_specular_col_pow;	


		Std( RendShaderManager *managerp ) :
			_managerp(managerp),
			_global_specular_coe(0.5f)
		{
		}

		void	Initialize();

		void	SetGlobalSpecularScale( float spec_scale )
		{
			_global_specular_coe = spec_scale;
		}
		float	GetGlobalSpecularScale() const
		{
			return _global_specular_coe;
		}

		void	SetBaseColor(	const PMath::Vector4 &diffuse_col,
								const PMath::Vector3 &specular_col, float spec_pow,
								const PMath::Vector4 &emissive_col );

		void	SetDiffuseTexture( TextureBase *texturep );

		void	SetFogColor( const PMath::Vector3 &fog_col );
		void	SetFogRange( float fog_start_depth, float fog_end_depth );
		void	SetParticleScale( float part_scale );

		void	SelectShader( bool has_vtex, bool has_vcolor, bool has_vnorm );
		void	SelectPntSprtShader( bool has_vcolor, float max_size );

		void	SelectStdVDef( int tex, int col, int nor );

		static char *MakeStdVSName_s( char out_name[128], int tex, int col, int nor );
		static char *MakeStdPSName_s( char out_name[128], int tex, int nor );

		//	void	SetModelprjMatrix( const PMath::Matrix44 &modelprj_mat );
		void	SetSHLightMatrices( const PMath::Matrix44 rgb_sh_light_obj_m[3],
									const PMath::Vector4 &os_light_dir );
	} _Std;

#ifdef TARGET_D3D
	// ...
#elif defined(TARGET_OPENGL)
	CGcontext				_context;
	CGprofile				_vs_profile;
	CGprofile				_ps_profile;

	static void myCgErrorHandler_s( CGcontext context, CGerror error, void *data );
#endif

public:
	RendShaderManager();
	~RendShaderManager();

	//==================================================================
	void	Initialize( RendDevice *rdevicep, const TCHAR *datadirp );

	//==================================================================
	RendShader	*CreatePixelShaderFromSource( const char *sourcep, const char *entrypointp=NULL )
	{
		RendShader	*shaderp = new RendShader( this );
		shaderp->CreatePixelProgram( sourcep, entrypointp );
		return shaderp;
	}
	//==================================================================
	RendShader	*CreateVertexShaderFromSource( const char *sourcep, const char *entrypointp=NULL )
	{
		RendShader	*shaderp = new RendShader( this );
		shaderp->CreateVertexProgram( sourcep, entrypointp );
		return shaderp;
	}

	//==================================================================
	void	SetProjMatrix( const PMath::Matrix44 &proj_mat );
	void	SetModelviewMatrix0( const PMath::Matrix44 &modelview_mat );
	
	void	PushMainMatrices();
	void	PopMainMatrices();
	void	LoadMainMatrices( RendShader::Param proj_mat_param,
							 RendShader::Param modelview_mat_param );

	void	SetMatrix44( RendShader::Param mat_param, const PMath::Matrix44 &mat );

	void	BeginRender();
	void	EndRender();
	void	ResetState();

	void	SelectVertexShader( RendShader *shaderp );
	void	SelectPixelShader( RendShader *shaderp );

	void	SetTexture( RendShader::Param tex_param, TextureBase *texturep );




	void	SelectDownsample4x4ClampShader( u_int src_wd, u_int src_he );
	void	SelectBloomShader(	const PMath::Vector4 *samp_offsp,
								const PMath::Vector4 *samp_weightsp,
								float bright_threshold );
	void	SelectPlainTextureShader();
	void	SelectHDRFinalPassShader( const PMath::Vector4 &bloom_scale );

	void	SelectVertexStream( const void *stream_posp,
								const void *stream_texp,
								const void *stream_colp,
								const void *stream_norp,
								int stride );

	void	SelectVertexBuffer( class RendVertexBuffer *vbuffp );

	static void MakeBloomParams_s(	int dwTextureWidth, int dwTextureHeight, 
									float fAngle, PMath::Vector4 *pvTexCoordOffsets,
									PMath::Vector4 *pvColorWeights, float fDeviation,
									float fMultiplier );
};

#endif
